package org.spbsu.squaddie.grapher;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.rmi.CORBA.Util;
import javax.swing.JFrame;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.spbsu.squaddie.Worker;
import org.spbsu.squaddie.adapter.csv.Scanner;
import org.spbsu.squaddie.model.Event;
import org.spbsu.squaddie.model.Pair;
import org.spbsu.squaddie.model.Snapshot;
import org.spbsu.squaddie.model.TimeSerie;
import org.spbsu.squaddie.util.Utils;

import com.googlecode.fannj.Fann;

/**
 * Hello world!
 * 
 */
// 19-100-1 is the best now
public class Main {
	private static final String SOCCER_TRAINING_FOLDER = "D:/dev/projects/neural/svn/fracsoft/soccer/training";
	private static final String SOCCER_TEST_FOLDER = "D:/dev/projects/neural/svn/fracsoft/soccer/test";
	private static final String LIBRARY_FILENAME = "D:/dev/projects/neural/neural-workspace/grapher/lib/fannfloat.dll";
	private static final String ANN_FILENAME = "D:/dev/projects/neural/squaddie.ann";
	private static final String TRAINING_FILENAME = "D:/dev/projects/neural/squaddie-training.dat";
	private static final String TEST_FILENAME = "D:/dev/projects/neural/squaddie-test.dat";
	private static final String SERIES_TRAINING_FOLDER = "D:/dev/projects/neural/svn/series/";
	private static final String SERIES_TEST_FOLDER = "D:/dev/projects/neural/series-training/";
	private static final Double MIN = 1.0;
	private static final Double MAX = 23.0;

	public static void main(String[] args) {
		Scanner scanner = new Scanner();

		// System.out.println(timeSeries.size());
		//
		List<TimeSerie> testTimeSeries = scanner.scanFolder(SOCCER_TRAINING_FOLDER);
		List<Double> averages = calculateAverages(testTimeSeries.get(2), 0);
		Normalizer normalizer = new Normalizer();
		averages = normalizer.deductFirst(averages);
		
		createGraph(averages);
		
		createGraph(applySma(averages, 1));
		createGraph(applySma(averages, 2));
		createGraph(applySma(averages, 5));
	
		createGraph(applySma(averages, 10));
		
		createGraph(applySma(averages, 20));
		createGraph(applySma(averages, 30));
		createGraph(applySma(averages, 40));
		
		createGraph(applySma(averages, 50));
		createGraph(applySma(averages, 60));
		
		
	

//		Normalizer normalizer = new Normalizer();
//		
//		averages = normalizer.deductFirst(averages);
//		
//		createGraph(averages);
		
		
		
		
//		averages = Utils.thinList(averages, 60);
//		averages = averages.subList(0, 38);
//
//		for (int i = 0; i < 38; ++i) {
//			System.out.print(averages.get(i) + " ");
//		}
//		System.out.println();
//
//		System.load(LIBRARY_FILENAME);
//
//		Fann ann = new Fann(ANN_FILENAME);
//
//		List<Double> predictedAverages = predictMany(ann,
//				averages.subList(0, 19), 19);
//
//		for (int i = 0; i < 38; ++i) {
//			System.out.print(predictedAverages.get(i) + " ");
//		}
//		
//		createGraph(averages);
//		createGraph(predictedAverages);

		
		
		
		
		
		// Worker worker = new Worker();
		//
		// Integer input = -1;
		//
		// try {
		// input = System.in.read() - '0';
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
		//
		// if (input.equals(0)) {
		//
		// System.out.println("Creating training file...");
		// List<TimeSerie> trainingTimeSeries = scanner
		// .scanFolder(SOCCER_TRAINING_FOLDER);
		// List<List<Double>> trainingMeasures =
		// createMeasures(trainingTimeSeries);
		// createTrainingFile(TRAINING_FILENAME, trainingMeasures);
		//
		// } else if (input.equals(1)) {
		// System.out.println("Testing...");
		// List<TimeSerie> testTimeSeries = scanner
		// .scanFolder(SOCCER_TEST_FOLDER);
		// List<List<Double>> testMeasures = createMeasures(testTimeSeries);
		// checkAnn(ANN_FILENAME, testMeasures);
		// }

		System.out.println("End.");
	}
	
	public static List<Double> applySma(List<Double> input, Integer windowSize) {
		List<List<Double>> windows = getWindows(Utils.thinList(input, 60), windowSize, 1);
		List<Double> result = new ArrayList<>();
		
		for (List<Double> window : windows) {
			result.add(Utils.getArithmeticAverage(window));
		}
		
		return result;
	}

	public static List<Double> predictMany(Fann ann, List<Double> values,
			Integer predictionSize) {

		if (ann.getNumInputNeurons() != values.size()) {
			System.out.print("Invalid input data length.");
			return Collections.emptyList();
		}

		Integer inputSize = values.size();
		List<Double> result = new ArrayList<>();
		result.addAll(values);

		for (int i = 0; i < predictionSize; ++i) {
			Double predictedValue = predict(ann,
					result.subList(result.size() - inputSize, result.size()));
			result.add(predictedValue);
		}

		return result;
	}

	public static Double predict(Fann ann, List<Double> values) {

		if (ann.getNumInputNeurons() != values.size()) {
			System.out.print("Invalid input data length.");
			return Double.MIN_VALUE;
		}

		Normalizer normalizer = new Normalizer();

		values = normalizer.normalize(values, MIN, MAX);

		float[] annInput = new float[values.size()];

		for (int i = 0; i < values.size(); ++i) {
			annInput[i] = values.get(i).floatValue();
		}

		Double predictedValue = (double) ann.run(annInput)[0];

		return normalizer.denormalize(predictedValue, MIN, MAX);
	}

	public static void checkAnn(String annFilename, List<List<Double>> measures) {
		System.load(LIBRARY_FILENAME);

		Fann fann = new Fann(annFilename);
		Normalizer normalizer = new Normalizer();

		Integer measureLength = measures.get(0).size();
		Double errorSum = 0.0;

		Integer counter;
		for (counter = 0; counter < measures.size(); ++counter) {
			float[] test = new float[measureLength - 1];
			float trueAnswer = measures.get(counter)
					.get(measures.get(0).size() - 1).floatValue();
			trueAnswer = normalizer.denormalize((double) trueAnswer, MIN, MAX)
					.floatValue();

			System.out.println("---" + counter + "---");
			for (Integer i = 0; i < measureLength - 1; ++i) {
				test[i] = measures.get(counter).get(i).floatValue();
				System.out.print(normalizer.denormalize((double) test[i], MIN,
						MAX) + " ");
			}

			float annAnswer = fann.run(test)[0];
			annAnswer = normalizer.denormalize((double) annAnswer, MIN, MAX)
					.floatValue();
			float error = Math.abs(annAnswer - trueAnswer);

			System.out.println();
			System.out.println("Last answer: "
					+ normalizer.denormalize((double) test[measureLength - 2],
							MIN, MAX));
			System.out.println("Real answer: " + trueAnswer);
			System.out.println("Fann answer: " + annAnswer);
			System.out.println("Error value: " + error);

			errorSum += error;
		}

		System.out.println("Average error: " + errorSum / counter);
	}

	public static List<Double> calculateAverages(TimeSerie timeSerie,
			Integer eventIndex) {
		List<Double> averages = new ArrayList<>();

		for (Snapshot snapshot : timeSerie) {
			averages.add(Utils.getAverage(snapshot.get(eventIndex)));
		}
		return averages;
	}

	public static void createGraph(List<Double> averages) {
		XYSeries series = new XYSeries("recall-precision");

		for (int i = 0; i < averages.size(); ++i) {
			series.add(i, averages.get(i));
			// System.out.println(averages.get(i));
		}

		XYDataset xyDataset = new XYSeriesCollection(series);
		JFreeChart chart = ChartFactory.createXYLineChart("time-average",
				"time", "average", xyDataset, PlotOrientation.VERTICAL, true,
				true, true);
		JFrame frame = new JFrame("Graph");
		ChartPanel chartPanel = new ChartPanel(chart);

		frame.getContentPane().add(chartPanel);
		frame.setSize(800, 600);
		frame.setVisible(true);
	}

	public static List<List<Double>> createMeasures(List<TimeSerie> timeSeries) {
		List<TimeSerie> decimatedTimeSeries = new ArrayList<>();

		for (TimeSerie timeSerie : timeSeries) {
			decimatedTimeSeries.addAll(selectSnapshots(timeSerie, 20, 60, 1,
					200));
		}

		List<List<Double>> measures = new ArrayList<>();

		for (TimeSerie timeSerie : decimatedTimeSeries) {

			for (int i = 0; i < 3; ++i) {
				List<Event> events = new ArrayList<>();
				for (Snapshot snapshot : timeSerie) {
					events.add(snapshot.get(i));
				}

				List<Double> measure = createTraining(events);
				if (!measure.isEmpty()) {
					measures.add(measure);
				}
			}
		}

		return measures;
	}

	private static List<Double> createTraining(List<Event> events) {
		if (events == null || events.isEmpty()) {
			return Collections.emptyList();
		}

		List<Double> averages = new ArrayList<>();
		for (Event event : events) {

			averages.add(Utils.getAverage(event));
		}

		return new Normalizer().normalize(averages, MIN, MAX);
	}

	public static List<TimeSerie> selectSnapshots(TimeSerie input,
			Integer windowSize, Integer dilutionFactor, Integer windowShift,
			Integer maxShift) {

		if (maxShift > input.size()) {
			maxShift = input.size();
		}

		List<TimeSerie> results = new ArrayList<>();

		for (int shift = 0; shift < maxShift; shift += windowShift) {
			TimeSerie result = new TimeSerie();
			for (int i = shift; i < input.size(); ++i) {
				if (i % dilutionFactor == 0) {
					result.add(input.get(i));

					if (result.size() == windowSize) {
						results.add(result);
						break;
					}
				}
			}
		}

		return results;
	}
	
	public static List<List<Double>> getWindows(List<Double> input,
			Integer windowSize, Integer windowShift) {

		List<List<Double>> results = new ArrayList<>();

		for (int shift = 0; shift < input.size(); shift += windowShift) {
			List<Double> result = new ArrayList<>();
			for (int i = shift; i < input.size(); ++i) {
				
					result.add(input.get(i));

					if (result.size() == windowSize) {
						results.add(result);
						break;
					}

			}
		}

		return results;
	}

	public static void createTrainingFile(String filename,
			List<List<Double>> measures) {
		if (measures == null || measures.isEmpty()) {
			return;
		}

		File file = new File(filename);
		if (file.exists()) {
			file.delete();
		}

		PrintWriter printWriter = null;
		try {
			printWriter = new PrintWriter(file);

			printWriter.print(measures.size() + " ");
			printWriter.print((measures.get(0).size() - 1) + " ");
			printWriter.print(1);

			for (List<Double> line : measures) {
				if (line == null || line.isEmpty()) {
					return;
				}

				printWriter.println();
				for (int i = 0; i < line.size() - 1; ++i) {
					printWriter.print(line.get(i) + " ");
				}
				printWriter.println();
				printWriter.print(line.get(line.size() - 1));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (printWriter != null) {
				printWriter.close();
			}
		}
	}
}
